"""
Case Type   : CM_Tools
Case Name   : 故障测试，备节点信号量测试(19/18)
Create At   : 2025/5/26
Owner       : ningyali
Description :
    1.主节点执行业务
    2.查看集群状态
    3.kill -19挂起备节点数据库进程
    4.查看集群状态
    5.查看主节点事务执行情况
Expect      :
    1.执行成功
    2.执行成功
    3.挂起备节点成功
    4.备节点重新拉起
    5.主节点执行事务不阻塞（集群情况下，非同步备，或配置了最大可用模式为on）
History     :
"""

import os
import re
import time
import unittest

from testcase.utils.ComInstall import CmInstall
from testcase.utils.ComInstall import CmUnInstall
from testcase.utils.Common import Common
from testcase.utils.CommonSH import CommonSH
from testcase.utils.Constant import Constant
from testcase.utils.ComThread import ComThread
from testcase.utils.Logger import Logger
from yat.test import Node
from yat.test import macro


class CMServer(unittest.TestCase):
    def setUp(self):
        self.log = Logger()
        self.log.info(f'-----{os.path.basename(__file__)} start-----')
        self.pri_sh = CommonSH('PrimaryDbUser')
        self.root_nodes = (Node('PrimaryRoot'), Node('Standby1Root'),
                           Node('Standby2Root'))
        self.user_nodes = (Node('PrimaryDbUser'), Node('Standby1DbUser'),
                           Node('Standby2DbUser'))
        self.common = Common()
        self.constant = Constant()
        self.t_name = 't_cm_tools_0066'
        self.q_cmd = f"source {macro.DB_ENV_PATH};cm_ctl query -v -C -i -d"

        text = '----prestep1:安装cm;expect:执行成功----'
        self.log.info(text)
        self.install_pkg_path = os.path.dirname(macro.DB_SCRIPT_PATH)
        self.cm = CmInstall(self.install_pkg_path, self.pri_sh.node.ssh_user)
        res = self.cm.update_xml_for_cm(*self.root_nodes)
        self.assertGreater(res.count('cmDir'), 1, f'执行失败: {text}')

        res = self.cm.cm_install(self.user_nodes[0])
        self.assertIn('Install CM tool success', res, f'执行失败: {text}')

        text = '----prestep2:配置参数instance_phony_dead_restart_interval;' \
               'expect:执行成功----'
        self.log.info(text)
        set_cmd = f"source {macro.DB_ENV_PATH};" \
                  f"cm_ctl set --param --server -k " \
                  f"instance_phony_dead_restart_interval=10;" \
                  f"cm_ctl reload --param --server"
        set_res = self.common.get_sh_result(self.user_nodes[0], set_cmd)
        self.assertIn('set cm_server.conf success', set_res, f'执行失败: {text}')
        self.assertIn(
            'cm_ctl: reload cm_server.conf success', set_res, f'执行失败: {text}')

    def test_trans(self):
        text = '----step1:主节点执行业务;expect:执行成功----'
        self.log.info(text)
        sql = f"begin;" \
              f"create table {self.t_name}(id int,name varchar(100));" \
              f"insert into {self.t_name} values(generate_series(1,1000)," \
              f"'a_'||generate_series(1,1000));" \
              f"update {self.t_name} set name='cm' where id<200;" \
              f"delete from {self.t_name};" \
              f"select count(*) from {self.t_name};" \
              f"drop table {self.t_name} cascade;" \
              f"end;"
        pri_thread = ComThread(self.pri_sh.cycle_exec_sql,
                               args=(sql, 50, False, None, True))
        pri_thread.setDaemon(True)
        pri_thread.start()

        text = '----step2:查看集群状态;expect:执行成功----'
        self.log.info(text)
        q_cmd = f"source {macro.DB_ENV_PATH};" \
                f"cm_ctl query -v -C -i -d"
        q_res = self.common.get_sh_result(self.user_nodes[1], q_cmd)
        self.assertIn('cluster_state   : Normal', q_res, f'执行失败: {text}')

        text = '----step3:kill -19挂起备节点数据库进程;expect:挂起备节点成功----'
        self.log.info(text)
        kill_cmd = f"ps ux|grep -v grep|grep gaussdb|" \
                   f"grep {macro.DB_INSTANCE_PATH}|awk '{{{{print $2}}}}'|" \
                   f"xargs -I {{{{}}}} kill -19 {{{{}}}};" \
                   f"ps ux"
        kill_res = self.common.get_sh_result(self.user_nodes[1], kill_cmd)
        self.assertNotIn('error', kill_res.lower(), f'执行失败: {text}')

        text = '----step4:查看集群状态;expect:备节点重新拉起----'
        self.log.info(text)
        mid_flag = False
        final_flag = False
        for i in range(25):
            self.log.info(f'\n\n\n第{i + 1}次查看集群状态')
            q_res = self.common.get_sh_result(self.user_nodes[0], self.q_cmd)
            if q_res.count('Standby Normal') != 2:
                mid_flag = True
            if mid_flag and 'Primary Normal' in q_res and \
                    q_res.count('Standby Normal') == 2:
                final_flag = True
                break
            time.sleep(10)
        self.assertTrue(mid_flag and final_flag, f'执行失败: {text}')

        text = '----step5:查看主节点事务执行情况;' \
               'expect:主节点执行事务不阻塞（集群情况下，非同步备，或配置了最大可用模式为on）----'
        self.log.info(text)
        pri_thread.join(1)
        pri_res = pri_thread.get_result()
        self.log.info(set(pri_res))
        self.assertNotEqual(set(pri_res), set('COMMIT'), f'执行失败: {text}')

    def tearDown(self):
        text = '----poststep1:还原集群;expect:执行成功----'
        self.log.info(text)
        q_res = self.common.get_sh_result(self.user_nodes[0], self.q_cmd)
        if 'Standby Normal' in q_res.splitlines()[-1].split('|')[0]:
            switch_cmd = f"source {macro.DB_ENV_PATH}; cm_ctl switchover -a;"
            self.common.get_sh_result(self.user_nodes[0], switch_cmd)
            time.sleep(5)
            q_res = self.common.get_sh_result(self.user_nodes[0], self.q_cmd)

        text = '----poststep2:删除表;expect:执行成功----'
        self.log.info(text)
        sql = f"drop table if exists {self.t_name} cascade;"
        sql_res = self.pri_sh.execut_db_sql(sql)
        self.log.info(sql_res)

        text = '----poststep3:卸载cm;expect:执行成功----'
        self.log.info(text)
        self.un_cm = CmUnInstall(self.install_pkg_path,
                                 self.pri_sh.node.ssh_user)
        res = self.un_cm.cm_uninstall(
            self.user_nodes[0], '--deleteData --deleteBinary')

        self.assertTrue(
            'Primary Normal' in q_res and q_res.count('Standby Normal') == 2,
            f'执行失败: {text}')
        self.assertIn(self.constant.DROP_TABLE_SUCCESS, sql_res,
                      f'执行失败: {text}')
        self.assertIn('Uninstall CM tool success', res, f'执行失败: {text}')
        self.log.info(f'-----{os.path.basename(__file__)} end-----')
